#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
配置热更新功能模块
支持在不重启系统的情况下重新加载配置
"""

import os
import time
import threading
from typing import Callable, Optional
from pathlib import Path
from .config_manager import CategoryConfigManager


class ConfigHotReloader:
    """
    配置热更新器
    监控配置文件变化并自动或手动触发配置更新
    """
    
    def __init__(self, config_manager: CategoryConfigManager):
        """
        初始化配置热更新器
        
        Args:
            config_manager: 配置管理器实例
        """
        self._config_manager = config_manager
        self._config_file_path: Optional[str] = None
        self._last_modified_time: float = 0
        self._watcher_thread: Optional[threading.Thread] = None
        self._stop_event = threading.Event()
        self._update_callbacks: list[Callable[[dict], None]] = []
        self._error_callbacks: list[Callable[[Exception], None]] = []
    
    def set_config_file(self, file_path: str) -> None:
        """
        设置要监控的配置文件路径
        
        Args:
            file_path: 配置文件路径
            
        Raises:
            FileNotFoundError: 如果文件不存在
        """
        file = Path(file_path)
        if not file.exists():
            raise FileNotFoundError(f"配置文件不存在: {file_path}")
        
        self._config_file_path = file_path
        self._last_modified_time = file.stat().st_mtime
    
    def reload_config(self) -> dict:
        """
        手动重新加载配置
        
        Returns:
            更新后的配置字典
            
        Raises:
            RuntimeError: 如果没有设置配置文件
            Exception: 加载失败时抛出异常
        """
        if not self._config_file_path:
            raise RuntimeError("没有设置配置文件路径")
        
        try:
            # 重新加载配置
            new_config = self._config_manager.load_config(self._config_file_path)
            
            # 更新最后修改时间
            self._last_modified_time = os.path.getmtime(self._config_file_path)
            
            # 触发更新回调
            for callback in self._update_callbacks:
                try:
                    callback(new_config)
                except Exception as e:
                    print(f"配置更新回调执行失败: {str(e)}")
            
            return new_config
            
        except Exception as e:
            # 触发错误回调
            for callback in self._error_callbacks:
                try:
                    callback(e)
                except Exception:
                    pass
            
            raise
    
    def start_watching(self, interval: float = 5.0) -> None:
        """
        开始监控配置文件变化
        
        Args:
            interval: 检查间隔（秒）
            
        Raises:
            RuntimeError: 如果没有设置配置文件
        """
        if not self._config_file_path:
            raise RuntimeError("没有设置配置文件路径")
        
        # 如果已经在监控，先停止
        self.stop_watching()
        
        # 重置停止事件
        self._stop_event.clear()
        
        # 创建监控线程
        self._watcher_thread = threading.Thread(
            target=self._watch_config_file,
            args=(interval,),
            daemon=True
        )
        
        self._watcher_thread.start()
        print(f"开始监控配置文件: {self._config_file_path}，检查间隔: {interval}秒")
    
    def stop_watching(self) -> None:
        """
        停止监控配置文件变化
        """
        if self._watcher_thread and self._watcher_thread.is_alive():
            self._stop_event.set()
            self._watcher_thread.join(timeout=2.0)
            self._watcher_thread = None
            print("停止监控配置文件变化")
    
    def _watch_config_file(self, interval: float) -> None:
        """
        监控配置文件变化的内部方法
        
        Args:
            interval: 检查间隔（秒）
        """
        while not self._stop_event.is_set():
            try:
                # 检查文件是否存在
                if not os.path.exists(self._config_file_path):
                    print(f"警告: 配置文件不存在: {self._config_file_path}")
                    time.sleep(interval)
                    continue
                
                # 获取当前修改时间
                current_modified_time = os.path.getmtime(self._config_file_path)
                
                # 如果文件被修改
                if current_modified_time > self._last_modified_time:
                    print(f"检测到配置文件变化: {self._config_file_path}")
                    self.reload_config()
                    print("配置已成功更新")
                
            except Exception as e:
                print(f"监控配置文件时出错: {str(e)}")
                # 触发错误回调
                for callback in self._error_callbacks:
                    try:
                        callback(e)
                    except Exception:
                        pass
            
            # 等待下一次检查
            self._stop_event.wait(interval)
    
    def add_update_callback(self, callback: Callable[[dict], None]) -> None:
        """
        添加配置更新回调函数
        当配置成功更新后，这些回调函数将被调用
        
        Args:
            callback: 回调函数，接收更新后的配置作为参数
        """
        if callback not in self._update_callbacks:
            self._update_callbacks.append(callback)
    
    def remove_update_callback(self, callback: Callable[[dict], None]) -> None:
        """
        移除配置更新回调函数
        
        Args:
            callback: 要移除的回调函数
        """
        if callback in self._update_callbacks:
            self._update_callbacks.remove(callback)
    
    def add_error_callback(self, callback: Callable[[Exception], None]) -> None:
        """
        添加错误回调函数
        当配置更新失败时，这些回调函数将被调用
        
        Args:
            callback: 回调函数，接收异常对象作为参数
        """
        if callback not in self._error_callbacks:
            self._error_callbacks.append(callback)
    
    def remove_error_callback(self, callback: Callable[[Exception], None]) -> None:
        """
        移除错误回调函数
        
        Args:
            callback: 要移除的回调函数
        """
        if callback in self._error_callbacks:
            self._error_callbacks.remove(callback)
    
    def is_watching(self) -> bool:
        """
        检查是否正在监控配置文件
        
        Returns:
            是否正在监控
        """
        return self._watcher_thread is not None and self._watcher_thread.is_alive()


# 使用示例
if __name__ == '__main__':
    import tempfile
    import shutil
    
    try:
        # 创建测试目录
        test_dir = tempfile.mkdtemp()
        test_config_path = os.path.join(test_dir, "test_category_config.txt")
        
        # 复制原始配置文件用于测试
        original_config = "../../data/编码类目表.txt"
        if os.path.exists(original_config):
            shutil.copy2(original_config, test_config_path)
            print(f"复制测试配置文件到: {test_config_path}")
        else:
            print(f"警告: 原始配置文件不存在: {original_config}")
            # 创建一个简单的测试配置文件
            with open(test_config_path, 'w', encoding='utf-8') as f:
                f.write("""() 测试类目/#测试#/
<isrequired>
选项A/#选项#/
  <关键词=关键词1 OR 关键词2>
选项B/#选项#/
  <关键词=关键词3 OR 关键词4>
""")
        
        # 创建配置管理器和热更新器
        manager = CategoryConfigManager()
        reloader = ConfigHotReloader(manager)
        
        # 设置配置文件
        reloader.set_config_file(test_config_path)
        
        # 初始加载配置
        print("\n初始加载配置...")
        config = manager.load_config(test_config_path)
        print(f"初始配置加载完成，类目数量: {manager.get_categories_count()}")
        
        # 定义更新回调
        def on_config_update(new_config):
            print(f"配置更新回调被触发! 新的类目数量: {len(new_config)}")
        
        def on_config_error(error):
            print(f"配置更新错误回调被触发! 错误: {str(error)}")
        
        # 添加回调
        reloader.add_update_callback(on_config_update)
        reloader.add_error_callback(on_config_error)
        
        # 开始监控
        print("\n开始监控配置文件变化（按Ctrl+C停止）...")
        reloader.start_watching(interval=2.0)
        
        # 模拟修改配置文件
        print("\n3秒后将模拟修改配置文件...")
        time.sleep(3)
        
        print("修改配置文件，添加新类目...")
        with open(test_config_path, 'a', encoding='utf-8') as f:
            f.write("\n\n() 新增类目/#新增测试#/\n<isrequired>\n新增选项/#选项#/\n  <关键词=新增1 OR 新增2>\n")
        
        # 等待配置更新
        time.sleep(3)
        
        # 手动重新加载
        print("\n手动重新加载配置...")
        reloader.reload_config()
        
        # 停止监控
        print("\n停止监控...")
        reloader.stop_watching()
        
        print("\n配置热更新功能测试完成!")
        
    except KeyboardInterrupt:
        print("\n测试被用户中断")
    except Exception as e:
        print(f"测试失败: {str(e)}")
    finally:
        # 清理测试文件
        if 'test_dir' in locals() and os.path.exists(test_dir):
            shutil.rmtree(test_dir)
            print(f"清理测试目录: {test_dir}")